#ifndef FIELDBASE_HPP
#define FIELDBASE_HPP

#include "meshTypes_fwd.hpp"
#include "meshTypes.hpp"

namespace hms
{

template<typename FieldType>
class FieldTraits {};


template<typename FieldType>
class FieldBase : public CrtpBase<FieldType>
{
public:
	using MeshType = typename FieldTraits<FieldType>::MeshType;
	using ValueType = typename FieldTraits<FieldType>::ValueType;
	using ConstValueType = typename FieldTraits<FieldType>::ConstValueType;

	static_assert( std::is_base_of_v<MeshBase<MeshType>, MeshType> );

	constexpr static scalar cornerValue {0};

protected:
	std::experimental::observer_ptr<const MeshType> m_mesh {nullptr};

public:
	FieldBase() = default;
	
	FieldBase( const MeshBase<MeshType>& meshArg ) :
		m_mesh { std::experimental::make_observer( &(meshArg.derived()) ) }
	{}

	/* no slicing */
	FieldBase( FieldType&& ) = delete;
	FieldBase( FieldType&  ) = delete;
	FieldBase( const FieldType& ) = delete;

	const MeshType& mesh() const {
		assert(m_mesh && "Field does not have a mesh assigned to it!");
		return *m_mesh;
	}

	void mesh( const MeshBase<MeshType>& meshArg ){
		m_mesh.reset( &(meshArg.derived()) );
	}

	ValueType values(){ return this->derived().valuesImp(); }
	ConstValueType values() const { return this->derived().valuesImp(); }

	decltype(auto) domainValues(){
		return this->derived().domainValuesImp();
	}
	decltype(auto) domainValues() const {
		return this->derived().domainValuesImp();
	}

	Index nComponents() const { return this->derived().valuesImp().rows(); }

	bool empty() const { return this->values().size() == 0; }
};


} // end namespace hms


#endif